using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;

namespace Petra.Gis.DataServices.WKT
{
  public partial class GeometryFactory : IGeometryFactory
  {
    //private pointers to functions to store in geometry mapping array ;-)
    private delegate GeometryBase CreateGeometryFn(ref string inputStr, System.Type memberOf);
    private delegate string SaveGeometryFn(GeometryBase geometry);

    //geomeytry mapping arrays
    internal static Dictionary<WKTGeometryType, string> getWktName;
    private static Dictionary<WKTGeometryType, CreateGeometryFn> storageToGeometry;
    private static Dictionary<System.Type, SaveGeometryFn> geometryToStorage;

    static GeometryFactory()
    {

      //this dictionary MUST keep this order !!!
      //because getGeometryName function
      getWktName = new Dictionary<WKTGeometryType, string>();
      getWktName.Add(WKTGeometryType.GeometryCollection, "GEOMETRYCOLLECTION");
      getWktName.Add(WKTGeometryType.Point, "POINT");
      getWktName.Add(WKTGeometryType.LineString, "LINESTRING");
      getWktName.Add(WKTGeometryType.Polygon, "POLYGON");
      getWktName.Add(WKTGeometryType.MultiPoint, "MULTIPOINT");
      getWktName.Add(WKTGeometryType.MultiLineString, "MULTILINESTRING");
      getWktName.Add(WKTGeometryType.MultiPolygon, "MULTIPOLYGON");

      //setup storage to geometry functions
      storageToGeometry = new Dictionary<WKTGeometryType, CreateGeometryFn>();
      storageToGeometry.Add(WKTGeometryType.Point, new CreateGeometryFn(ToPoint));
      storageToGeometry.Add(WKTGeometryType.LineString, new CreateGeometryFn(ToLineString));
      storageToGeometry.Add(WKTGeometryType.Polygon, new CreateGeometryFn(ToPolygon));
      storageToGeometry.Add(WKTGeometryType.GeometryCollection, new CreateGeometryFn(ToGeometryCollection));
      storageToGeometry.Add(WKTGeometryType.MultiPoint, new CreateGeometryFn(ToMultiPoint));
      storageToGeometry.Add(WKTGeometryType.MultiLineString, new CreateGeometryFn(ToMultiLine));
      storageToGeometry.Add(WKTGeometryType.MultiPolygon, new CreateGeometryFn(ToMultiPolygon));


      //setup geometry to storage functions
      geometryToStorage = new Dictionary<System.Type,SaveGeometryFn>();
      geometryToStorage.Add(typeof(Point), new SaveGeometryFn(PointToString));
      //geometryToStorage.Add(typeof(Label), new SaveGeometryFn(labelToString));
      geometryToStorage.Add(typeof(LineString), new SaveGeometryFn(LineStringToString));
      geometryToStorage.Add(typeof(Polygon), new SaveGeometryFn(PolygonToString));
      //geometryToStorage.Add(typeof(Arc), new SaveGeometryFn(arcToString));
      //geometryToStorage.Add(typeof(LineRing), new SaveGeometryFn(LineRingToString));

      geometryToStorage.Add(typeof(GeometryCollection), new SaveGeometryFn(GeometryCollectionToString));
      geometryToStorage.Add(typeof(MultiPoint), new SaveGeometryFn(MultiPointToString));
      //geometryToStorage.Add(typeof(MultiLabel), new SaveGeometryFn(multiLineToString));
      geometryToStorage.Add(typeof(MultiLine), new SaveGeometryFn(MultiLineToString));
      geometryToStorage.Add(typeof(MultiArea), new SaveGeometryFn(MultiPolygonToString));

      //geometryToStorage.Add(typeof(Path), new SaveGeometryFn(pathToString));
      //geometryToStorage.Add(typeof(PathRing), new SaveGeometryFn(pathRingToString));
    }


    //-----------------------begin interface functions block--------------------------

    public GeometryBase CreateGeometry(ref object vData)
    {
      string g = (string)vData;

      return _createGeometry(ref g, null);
    }


    public GeometryBase CreateGeometry(object vData)
    {
      string g = (string)vData;

      return _createGeometry(ref g, null);
    }

    //interface function saveGeometry
    public object SaveGeometry(GeometryBase oGeom)
    { 
      return (object)_saveGeometryBlob(oGeom);
    }

    //interface function canSave
    public bool CanSave(GeometryBase geometry)
    {
      //search Dictionary for delegate for this geometry
      try
      {
        SaveGeometryFn d = geometryToStorage[geometry.GetType()];
      }
      catch {
        return false;
      }
      return true;
    }
    //-----------------------end interface functions block--------------------------



    /*
     internal implementation of createGeometry
     if this function is called from inside of geometry (Boundary, Collection, CompositePolygon, CompositePolyline)
     arrayOffset must point to begin of subgeometry
     and parentGeomType defines relations, WKT has different definition then SFS, must perform check and conversion !!
    */
    private static GeometryBase _createGeometry(ref string inputStr, System.Type parentGeomType)
    {
      WKTGeometryType gType;

      gType = _getGeometryName(ref inputStr);

      //call function in dictionary that will create geometry from byte[]
      return storageToGeometry[gType](ref inputStr, parentGeomType);

    }


    private static string _saveGeometryBlob(GeometryBase geometry)
    {

      return geometryToStorage[geometry.GetType()](geometry);
    }




    private static WKTGeometryType _getGeometryName(ref string inputStr)
    {
      string pattern;
      foreach (KeyValuePair<WKTGeometryType, string> kvp in getWktName)
      {
        pattern = @"(?<geometryname>^[w]*" + kvp.Value + ")";
        //pattern = @"^[w]*" + kvp.Value;

        Regex r = new Regex(pattern);
        if (r.IsMatch(inputStr))
          return kvp.Key;
      }
      throw new ArgumentException("invalid input string, not a WKB text", "inputStr");
    }
    
  }

}
